import typing
from enum import Enum

from pydantic import BaseModel, Field, IPvAnyAddress, IPvAnyNetwork


class XCPDataType(Enum):
    """
    变量的数据类型
    """
    UBYTE = 1
    SBYTE = 2
    UWORD = 3
    SWORD = 4
    ULONG = 5
    SLONG = 6
    A_UINT64 = 7
    A_INT64 = 8
    FLOAT32_IEEE = 9
    # FLOAT64_IEEE = 10
    
class XCPRecordLayout(Enum):
    """
    变量的内存布局
    """
    Scalar_BOOLEAN = 1
    Scalar_UBYTE = 2
    Scalar_SBYTE = 3
    Scalar_UWORD = 4
    Scalar_SWORD = 5
    Scalar_ULONG = 6
    Scalar_SLONG = 7
    Scalar_A_UINT64 = 8
    Scalar_A_INT64 = 9
    Scalar_FLOAT32_IEEE = 10
    # Scalar_FLOAT64_IEEE = 11


class XCPA2lMearsurement(BaseModel):
    """
    观测量的数据结构描述
    """
    name: str = Field(
        description="在 XCP slave 的观测量名称"
    )
    long_identifier: typing.Optional[str] = Field(
        default="",
        description="关于该观测量的解释信息"
    )
    data_type: XCPDataType = Field(
        description="该观测量的数据类型"
    )
    lower_limit: typing.Optional[float] = Field(
        default=None,
        description="该观测量的最小值，如果不填就根据数据类型推断"
    )
    upper_limit: typing.Optional[float] = Field(
        default=None,
        description="该观测量的最大值，如果不填就根据数据类型推断"
    )
    address: typing.Optional[int] = Field(
        default=None,
        description="该观测量的地址"
    )
    

class XCPA2lCharacteristic(BaseModel):
    """
    标定量的数据结构描述
    """
    name: str = Field(
        description="在 XCP slave 的标定量名称"
    )
    long_identifier: typing.Optional[str] = Field(
        default="",
        description="关于该变量的解释信息"
    )
    record_layout: XCPRecordLayout = Field(
        description="该变量的内存布局"
    )
    lower_limit: typing.Optional[float] = Field(
        default=None,
        description="该变量的最小值"
    )
    upper_limit: typing.Optional[float] = Field(
        default=None,
        description="该变量的最大值"
    )
    address: typing.Optional[int] = Field(
        default=None,
        description="该标定量的地址"
    )
    
class XCPMearsurement(BaseModel):
    """
    描述 XCP slave 运行时的观测量
    """
    name:str = Field(
        description="观测量的名称"
    )
    current_value: typing.Union[int, float, None] = Field(
        default=None,
        description="当前观测量的值, 需要在用户赋值"
    )
    data_type: typing.Optional[XCPDataType] = Field(
        default=None,
        description="该观测量的数据类型"
    )
    
class XCPCharacteristic(BaseModel):
    """
    描述 XCP slave 运行时的标定量
    """
    name:str = Field(
        description="标定量的名称"
    )
    new_value: typing.Union[int, float] = Field(
        description="新的标定量的值"
    )
    old_value: typing.Union[int, float] = Field(
        description="旧的标定量的值"
    )
    
class XCPIfParameters(BaseModel):
    ip: typing.Optional[str] = \
        Field(
            default=None, 
            description="XCP从节点的 IP 地址，默认是 None, 如果是 None 会自动推断使用的 IP 地址，但是有可能和期望的 IP 不一样",
            title="XCP从节点的 IP 地址")
    
class XCPBridgeMetaData(BaseModel):
    xcp_if_parameters:         XCPIfParameters                      = XCPIfParameters()
    mearsurements:             typing.List[XCPA2lMearsurement]         = []
    characteristics:           typing.List[XCPA2lCharacteristic]       = []

class XCPBridgeInitData(BaseModel):
    """
    XCP slave 启动时要初始化的数据
    主要包括：
    1. a2l 文件
    2. 对应的 hex 文件
    3. 修改标定量后调用的回调函数
    4. 读取观测量的回调函数
    """
    a2l_file_path:str = Field(
        description="XCP Bridge 启动时要加载的 a2l 文件的路径"
    )
    hex_file_path:str = Field(
        description="XCP Bridge 启动时要加载的 hex 文件的路径"
    )
    cb_on_calibration_changed:typing.Optional[typing.Callable[[XCPCharacteristic], None]] = Field(
        default=None,
        description="修改标定量后调用的回调函数"
    )
    cb_on_read_mearsurement:typing.Optional[typing.Callable[[XCPMearsurement], None]] = Field(
        default=None,
        description="读取观测量的回调函数"
    )
    logger_level:str = Field(
        default="INFO",
        description="日志的级别"
    )
    
class MemoryData(BaseModel):
    """
    """
    address_start: int = Field(
        description="内存的起始地址"
    )
    data_list: typing.List[int] = Field(
        description="内存的数据列表"
    )
    address_end: int = Field(
        default=0x00,
        description="内存的结束地址"
    )
